
// ================================================================================================
// -*- C++ -*-
// File: Ant.cpp
// Author: Guilherme R. Lampert
// Created on: 17/09/13
// Brief: Represents one of our ants.
// ================================================================================================

#include "WorldState.hpp"
#include "Ant.hpp"

// This must be set by WorldState at startup.
// It is read-only shared by all ants.
int Ant::viewDistance = 0;

Ant::Ant(int x, int y, WorldState * ws) :
	state(ws),
	timeForComplexSeach(0),
	timeForNewRand(0),
	currentRandDir(0)
{
	location.x = x;
	location.y = y;

	goal.mapLocation.x = -1;
	goal.mapLocation.y = -1;
	goal.expectedContents = -1;
	goal.active = false;
}

bool Ant::ShouldDoComplexSearch() const
{
	++timeForComplexSeach;
	if (timeForComplexSeach == 3)
	{
		// Every 3 turns do a search
		timeForComplexSeach = 0;
		return (true);
	}

	return (false);
}

bool Ant::HasLineOfSightTo(const MapCellContents lookFor[], int num, Location & loc, MapCellContents & found) const
{
	DBG_ASSERT(viewDistance > 0);

	const int maxY = state->MaxMapY();
	const int maxX = state->MaxMapX();

	int i;
	int x;
	int y;

	// +X axis:
	for (i = 0, x = location.x; ((i < viewDistance) && (x < maxX)); ++i, ++x)
	{
		const MapCell & cell = state->MapCellAt(x, location.y);
		if (CellIsWater(cell))
		{
			break; // If there is water ahead, can't move in this direction.
		}
		for (int n = 0; n < num; ++n)
		{
			if ((cell.contents == lookFor[n]) && (cell.player != 0))
			{
				loc.x = x;
				loc.y = location.y;
				found = lookFor[n];
				return (true);
			}
		}
	}

	// -X axis:
	for (i = 0, x = location.x; ((i < viewDistance) && (x >= 0)); ++i, --x)
	{
		const MapCell & cell = state->MapCellAt(x, location.y);
		if (CellIsWater(cell))
		{
			break; // If there is water ahead, can't move in this direction.
		}
		for (int n = 0; n < num; ++n)
		{
			if ((cell.contents == lookFor[n]) && (cell.player != 0))
			{
				loc.x = x;
				loc.y = location.y;
				found = lookFor[n];
				return (true);
			}
		}
	}

	// +Y axis:
	for (i = 0, y = location.y; ((i < viewDistance) && (y < maxY)); ++i, ++y)
	{
		const MapCell & cell = state->MapCellAt(location.x, y);
		if (CellIsWater(cell))
		{
			break; // If there is water ahead, can't move in this direction.
		}
		for (int n = 0; n < num; ++n)
		{
			if ((cell.contents == lookFor[n]) && (cell.player != 0))
			{
				loc.x = location.x;
				loc.y = y;
				found = lookFor[n];
				return (true);
			}
		}
	}

	// -Y axis:
	for (i = 0, y = location.y; ((i < viewDistance) && (y >= 0)); ++i, --y)
	{
		const MapCell & cell = state->MapCellAt(location.x, y);
		if (CellIsWater(cell))
		{
			break; // If there is water ahead, can't move in this direction.
		}
		for (int n = 0; n < num; ++n)
		{
			if ((cell.contents == lookFor[n]) && (cell.player != 0))
			{
				loc.x = location.x;
				loc.y = y;
				found = lookFor[n];
				return (true);
			}
		}
	}

	return (false);
}

void Ant::MoveToGoal()
{
	const Location currentLocation = {{location.x, location.y}};
	char moveDirection = 0; // {N,S,E,W}

	Location newLocation = {{currentLocation.x, currentLocation.y}};
	Location destination;

	if (path.empty())
	{
		// Empty path, following a simple strait.
		// This happens when HasLineOfSightTo() generated a goal.
		destination = goal.mapLocation;
	}
	else
	{
		// Following a complex path that might involve turns.
		// This happens when a goal is defined by a graph search.
		const int nodeIndex = path.front();
		destination = state->GetGraphNode(nodeIndex).location;
	}

	if (destination.x > newLocation.x)
	{
		newLocation.x++;
		moveDirection = 'E';
	}
	else if (destination.x < newLocation.x)
	{
		newLocation.x--;
		moveDirection = 'W';
	}

	if (destination.y > newLocation.y)
	{
		newLocation.y++;
		moveDirection = 'S';
	}
	else if (destination.y < newLocation.y)
	{
		newLocation.y--;
		moveDirection = 'N';
	}

	// Output move information:
	const bool didMove = DoMove(currentLocation, newLocation, moveDirection);

	if (didMove)
	{
		if (path.empty())
		{
			// Path was not obstructed, 'location' was updated:
			if ((location.x == destination.x) && (location.y == destination.y))
			{
				// Reached goal!
				goal.active = false;
			}
		}
		else
		{
			// If we moved then remove this node from the path,
			// since it was visited.
			path.pop_front();

			if (path.empty())
			{
				// Finished following path. Goal reached.
				goal.active = false;
			}
		}
	}
}

void Ant::RandomMove()
{
	// Every N turns do a rand() and get a new random direction:
	static const int TurnsTilNextRand = 7;

	const Location currentLocation = {{location.x, location.y}};
	Location newLocation = {{currentLocation.x, currentLocation.y}};

	if (timeForNewRand == 0)
	{
		// Possibly change direction:
		timeForNewRand = TurnsTilNextRand;
		switch ((rand() + 1) % 4)
		{
		case 0 :
			currentRandDir = 'N';
			break;
		case 1 :
			currentRandDir = 'S';
			break;
		case 2 :
			currentRandDir = 'E';
			break;
		case 3 :
			currentRandDir = 'W';
			break;
		default :
			break;
		}
	}

	switch (currentRandDir)
	{
	case 'N' :
		newLocation.y--;
		break;
	case 'S' :
		newLocation.y++;
		break;
	case 'E' :
		newLocation.x++;
		break;
	case 'W' :
		newLocation.x--;
		break;
	default :
		break;
	}

	--timeForNewRand;

	DoMove(currentLocation, newLocation, currentRandDir);
}

bool Ant::DoMove(Location currentLocation, Location newLocation, int direction)
{
	if (!direction)
	{
		return (false);
	}

	if (newLocation.x < 0)
	{
		newLocation.x = 0;
	}
	if (newLocation.y < 0)
	{
		newLocation.y = 0;
	}
	if (newLocation.x >= state->MaxMapX())
	{
		newLocation.x = state->MaxMapX()-1;
	}
	if (newLocation.y >= state->MaxMapY())
	{
		newLocation.y = state->MaxMapY()-1;
	}

	MapCell & newCell = state->MapCellAt(newLocation.x, newLocation.y);
	if (CellIsAnt(newCell) && (newCell.player == 0))
	{
		// Cell we want to go to is occupied by a friendly ant,
		// can't move there, this would destroy both ants!
		return (false);
	}

	// Update cells and this ant's position:
	MapCell & currentCell = state->MapCellAt(currentLocation.x, currentLocation.y);
	currentCell.player   = -1; // Nobody
	currentCell.contents =  0; // Nothing
	currentCell.visible  =  1; // Still visible

	// New cell gets this ant:
	newCell.player   = 0; // Me
	newCell.contents = CC_ANT;
	newCell.visible  = 1;

	// And update the internal location:
	location.x = newLocation.x;
	location.y = newLocation.y;

	// Command format: From current location move in the given direction, resulting in 'newLocation':
	std::cout << "o " << currentLocation.y << " " << currentLocation.x << " " << static_cast<char>(direction) << std::endl;
	return (true);
}

void Ant::DebugDump() const
{
	LOG("---------- Ant dump: -----------");
	LOG("state..................: " << reinterpret_cast<const void *>(state));
	LOG("timeForNewRand.........: " << timeForNewRand);
	LOG("currentRandDir.........: " << static_cast<char>(currentRandDir));
	LOG("location.x.............: " << location.x);
	LOG("location.y.............: " << location.y);
	LOG("goal.mapLocation.x.....: " << goal.mapLocation.x);
	LOG("goal.mapLocation.y.....: " << goal.mapLocation.y);
	LOG("goal.expectedContents..: " << goal.expectedContents);
	LOG("goal.active............: " << goal.active);
	LOG("path.size..............: " << path.size());
	LOG("--------------------------------");
}
